﻿using UnityEngine;
using System.Collections.Generic;

public class ThreatEngine
{
    private ThreatCell[,] mThreatArray;
    private ThreatCell[,] mBufferedThreatArray;
    private ThreatCell[,] mTotalThreatArray;
    private readonly int mSize_X, mSize_Y;

    public ThreatEngine(int size_x, int size_y)
    {
        mSize_X = size_x;
        mSize_Y = size_y;
        mThreatArray = new ThreatCell[mSize_X, mSize_Y];
        mBufferedThreatArray = new ThreatCell[mSize_X, mSize_Y];
        mTotalThreatArray = new ThreatCell[mSize_X, mSize_Y];
        for (int x = 0; x < mSize_X; x++)
        {
            for (int y = 0; y < mSize_Y; y++)
            {
                mBufferedThreatArray[x, y] = new ThreatCell();
                mThreatArray[x, y] = new ThreatCell();
                mTotalThreatArray[x, y] = new ThreatCell();
            }
        }
    }

    public void Update()
    {
        for (int x = 0; x < mSize_X; x++)
        {
            for (int y = 0; y < mSize_Y; y++)
            {
                // Quad 1
                DirectionalThreatCell originalCell = mBufferedThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_PosY];
                originalCell.Reset();
                // Bottom Cell
                if (y != 0)
                {
                    DirectionalThreatCell otherCell = mThreatArray[x, y - 1].mDirectionThreatCells[(int)DirectionEnum.PosX_PosY];
                    float threatX = otherCell.mDirectionalThreat_X;
                    float threatY = otherCell.mDirectionalThreat_Y + otherCell.mAbsoluteThreat;
                    float denom = (threatY + threatX);
                    if (denom > .05f)
                    {
                        float threatRatio = threatY / (threatY + threatX) * (1 - mThreatArray[x, y].mThreatDampening);
                        originalCell.mAbsoluteThreat += threatRatio * otherCell.mAbsoluteThreat;
                        originalCell.mDirectionalThreat_X += threatRatio * threatX;
                        originalCell.mDirectionalThreat_Y += threatRatio * threatY;
                        if (originalCell.mAbsoluteThreat > .1f)
                        {
                            float tot_x = originalCell.mDirectionalThreat_X;
                            float tot_y = originalCell.mDirectionalThreat_Y;
                            float sqrtTot = Mathf.Sqrt(tot_x * tot_x + tot_y * tot_y);
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_PosY].mAbsoluteThreat += originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_PosY].mDirectionalThreat_X += tot_x / sqrtTot * originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_PosY].mDirectionalThreat_Y += tot_y / sqrtTot * originalCell.mAbsoluteThreat;
                        }
                    }
                }
                // Left Cell
                if (x != 0)
                {
                    DirectionalThreatCell otherCell = mThreatArray[x - 1, y].mDirectionThreatCells[(int)DirectionEnum.PosX_PosY];
                    float threatX = otherCell.mDirectionalThreat_X + otherCell.mAbsoluteThreat;
                    float threatY = otherCell.mDirectionalThreat_Y;
                    float denom = (threatY + threatX);
                    if (denom > .05f)
                    {
                        float threatRatio = threatX / (threatY + threatX) * (1 - mThreatArray[x, y].mThreatDampening);
                        originalCell.mAbsoluteThreat += threatRatio * otherCell.mAbsoluteThreat;
                        originalCell.mDirectionalThreat_X += threatRatio * threatX;
                        originalCell.mDirectionalThreat_Y += threatRatio * threatY;
                        if (originalCell.mAbsoluteThreat > .1f)
                        {
                            float tot_x = originalCell.mDirectionalThreat_X;
                            float tot_y = originalCell.mDirectionalThreat_Y;
                            float sqrtTot = Mathf.Sqrt(tot_x * tot_x + tot_y * tot_y);
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_PosY].mAbsoluteThreat += originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_PosY].mDirectionalThreat_X += tot_x / sqrtTot * originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_PosY].mDirectionalThreat_Y += tot_y / sqrtTot * originalCell.mAbsoluteThreat;
                        }
                    }
                }
                originalCell.mAbsoluteThreat += mThreatArray[x, y].mPendingThreat;
                // Quad 2
                originalCell = mBufferedThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_PosY];
                originalCell.Reset();
                // Bottom Cell
                if (y != 0)
                {
                    DirectionalThreatCell otherCell = mThreatArray[x, y - 1].mDirectionThreatCells[(int)DirectionEnum.NegX_PosY];
                    float threatX = otherCell.mDirectionalThreat_X;
                    float threatY = otherCell.mDirectionalThreat_Y + otherCell.mAbsoluteThreat;
                    float denom = (threatY + threatX);
                    if (denom > .05f)
                    {
                        float threatRatio = threatY / (threatY + threatX) * (1 - mThreatArray[x, y].mThreatDampening);
                        originalCell.mAbsoluteThreat += threatRatio * otherCell.mAbsoluteThreat;
                        originalCell.mDirectionalThreat_X += threatRatio * threatX;
                        originalCell.mDirectionalThreat_Y += threatRatio * threatY;
                        if (originalCell.mAbsoluteThreat > .1f)
                        {
                            float tot_x = originalCell.mDirectionalThreat_X;
                            float tot_y = originalCell.mDirectionalThreat_Y;
                            float sqrtTot = Mathf.Sqrt(tot_x * tot_x + tot_y * tot_y);
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_PosY].mAbsoluteThreat += originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_PosY].mDirectionalThreat_X += tot_x / sqrtTot * originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_PosY].mDirectionalThreat_Y += tot_y / sqrtTot * originalCell.mAbsoluteThreat;
                        }
                    }
                }
                // Right Cell
                if (x != mSize_X - 1)
                {
                    DirectionalThreatCell otherCell = mThreatArray[x + 1, y].mDirectionThreatCells[(int)DirectionEnum.NegX_PosY];
                    float threatX = otherCell.mDirectionalThreat_X + otherCell.mAbsoluteThreat;
                    float threatY = otherCell.mDirectionalThreat_Y;
                    float denom = (threatY + threatX);
                    if (denom > .05f)
                    {
                        float threatRatio = threatX / (threatY + threatX) * (1 - mThreatArray[x, y].mThreatDampening);
                        originalCell.mAbsoluteThreat += threatRatio * otherCell.mAbsoluteThreat;
                        originalCell.mDirectionalThreat_X += threatRatio * threatX;
                        originalCell.mDirectionalThreat_Y += threatRatio * threatY;
                        if (originalCell.mAbsoluteThreat > .1f)
                        {
                            float tot_x = originalCell.mDirectionalThreat_X;
                            float tot_y = originalCell.mDirectionalThreat_Y;
                            float sqrtTot = Mathf.Sqrt(tot_x * tot_x + tot_y * tot_y);
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_PosY].mAbsoluteThreat += originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_PosY].mDirectionalThreat_X += tot_x / sqrtTot * originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_PosY].mDirectionalThreat_Y += tot_y / sqrtTot * originalCell.mAbsoluteThreat;
                        }
                    }
                }
                originalCell.mAbsoluteThreat += mThreatArray[x, y].mPendingThreat;
                // Quad 3
                originalCell = mBufferedThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_NegY];
                originalCell.Reset();
                // Top Cell
                if (y != mSize_Y - 1)
                {
                    DirectionalThreatCell otherCell = mThreatArray[x, y + 1].mDirectionThreatCells[(int)DirectionEnum.NegX_NegY];
                    float threatX = otherCell.mDirectionalThreat_X;
                    float threatY = otherCell.mDirectionalThreat_Y + otherCell.mAbsoluteThreat;
                    float denom = (threatY + threatX);
                    if (denom > .05f)
                    {
                        float threatRatio = threatY / (threatY + threatX) * (1 - mThreatArray[x, y].mThreatDampening);
                        originalCell.mAbsoluteThreat += threatRatio * otherCell.mAbsoluteThreat;
                        originalCell.mDirectionalThreat_X += threatRatio * threatX;
                        originalCell.mDirectionalThreat_Y += threatRatio * threatY;
                        if (originalCell.mAbsoluteThreat > .1f)
                        {
                            float tot_x = originalCell.mDirectionalThreat_X;
                            float tot_y = originalCell.mDirectionalThreat_Y;
                            float sqrtTot = Mathf.Sqrt(tot_x * tot_x + tot_y * tot_y);
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_NegY].mAbsoluteThreat += originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_NegY].mDirectionalThreat_X += tot_x / sqrtTot * originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_NegY].mDirectionalThreat_Y += tot_y / sqrtTot * originalCell.mAbsoluteThreat;
                        }
                    }
                }
                // Right Cell
                if (x != mSize_X - 1)
                {
                    DirectionalThreatCell otherCell = mThreatArray[x + 1, y].mDirectionThreatCells[(int)DirectionEnum.NegX_NegY];
                    float threatX = otherCell.mDirectionalThreat_X + otherCell.mAbsoluteThreat;
                    float threatY = otherCell.mDirectionalThreat_Y;
                    float denom = (threatY + threatX);
                    if (denom > .05f)
                    {
                        float threatRatio = threatX / (threatY + threatX) * (1 - mThreatArray[x, y].mThreatDampening);
                        originalCell.mAbsoluteThreat += threatRatio * otherCell.mAbsoluteThreat;
                        originalCell.mDirectionalThreat_X += threatRatio * threatX;
                        originalCell.mDirectionalThreat_Y += threatRatio * threatY;
                        if (originalCell.mAbsoluteThreat > .1f)
                        {
                            float tot_x = originalCell.mDirectionalThreat_X;
                            float tot_y = originalCell.mDirectionalThreat_Y;
                            float sqrtTot = Mathf.Sqrt(tot_x * tot_x + tot_y * tot_y);
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_NegY].mAbsoluteThreat += originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_NegY].mDirectionalThreat_X += tot_x / sqrtTot * originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.NegX_NegY].mDirectionalThreat_Y += tot_y / sqrtTot * originalCell.mAbsoluteThreat;
                        }
                    }
                }
                originalCell.mAbsoluteThreat += mThreatArray[x, y].mPendingThreat;
                // Quad 4
                originalCell = mBufferedThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_NegY];
                originalCell.Reset();
                // Top Cell
                if (y != mSize_Y - 1)
                {
                    DirectionalThreatCell otherCell = mThreatArray[x, y + 1].mDirectionThreatCells[(int)DirectionEnum.PosX_NegY];
                    float threatX = otherCell.mDirectionalThreat_X;
                    float threatY = otherCell.mDirectionalThreat_Y + otherCell.mAbsoluteThreat;
                    float denom = (threatY + threatX);
                    if (denom > .05f)
                    {
                        float threatRatio = threatY / (threatY + threatX) * (1 - mThreatArray[x, y].mThreatDampening);
                        originalCell.mAbsoluteThreat += threatRatio * otherCell.mAbsoluteThreat;
                        originalCell.mDirectionalThreat_X += threatRatio * threatX;
                        originalCell.mDirectionalThreat_Y += threatRatio * threatY;
                        if (originalCell.mAbsoluteThreat > .1f)
                        {
                            float tot_x = originalCell.mDirectionalThreat_X;
                            float tot_y = originalCell.mDirectionalThreat_Y;
                            float sqrtTot = Mathf.Sqrt(tot_x * tot_x + tot_y * tot_y);
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_NegY].mAbsoluteThreat += originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_NegY].mDirectionalThreat_X += tot_x / sqrtTot * originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_NegY].mDirectionalThreat_Y += tot_y / sqrtTot * originalCell.mAbsoluteThreat;
                        }
                    }
                }
                // Right Cell
                if (x != 0)
                {
                    DirectionalThreatCell otherCell = mThreatArray[x - 1, y].mDirectionThreatCells[(int)DirectionEnum.PosX_NegY];
                    float threatX = otherCell.mDirectionalThreat_X + otherCell.mAbsoluteThreat;
                    float threatY = otherCell.mDirectionalThreat_Y;
                    float denom = (threatY + threatX);
                    if (denom > .05f)
                    {
                        float threatRatio = threatX / (threatY + threatX) * (1 - mThreatArray[x, y].mThreatDampening);
                        originalCell.mAbsoluteThreat += threatRatio * otherCell.mAbsoluteThreat;
                        originalCell.mDirectionalThreat_X += threatRatio * threatX;
                        originalCell.mDirectionalThreat_Y += threatRatio * threatY;
                        if (originalCell.mAbsoluteThreat > .1f)
                        {
                            float tot_x = originalCell.mDirectionalThreat_X;
                            float tot_y = originalCell.mDirectionalThreat_Y;
                            float sqrtTot = Mathf.Sqrt(tot_x * tot_x + tot_y * tot_y);
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_NegY].mAbsoluteThreat += originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_NegY].mDirectionalThreat_X += tot_x / sqrtTot * originalCell.mAbsoluteThreat;
                            mTotalThreatArray[x, y].mDirectionThreatCells[(int)DirectionEnum.PosX_NegY].mDirectionalThreat_Y += tot_y / sqrtTot * originalCell.mAbsoluteThreat;
                        }
                    }
                }
                originalCell.mAbsoluteThreat += mThreatArray[x, y].mPendingThreat;
                mThreatArray[x, y].mPendingThreat = 0;
            }
        }
        // Swap buffers.
        ThreatCell[,] temp = mBufferedThreatArray;
        mBufferedThreatArray = mThreatArray;
        mThreatArray = temp;
    }

    public Vector3 GetCellDirection(int x, int y, DirectionEnum direction)
    {
        Vector3 answer = new Vector3();
        switch ((int)direction)
        {
            case 0:
                answer.x = 1;
                answer.z = 1;
                break;
            case 1:
                answer.x = 1;
                answer.z = -1;
                break;
            case 2:
                answer.x = -1;
                answer.z = 1;
                break;
            case 3:
                answer.x = -1;
                answer.z = -1;
                break;
        }
        answer.x *= mTotalThreatArray[x, y].mDirectionThreatCells[(int)direction].mDirectionalThreat_X;
        answer.z *= mTotalThreatArray[x, y].mDirectionThreatCells[(int)direction].mDirectionalThreat_Y;
        answer.Normalize();
        answer *= mTotalThreatArray[x, y].mDirectionThreatCells[(int)direction].mAbsoluteThreat;
        return answer / 2;
    }

    public void SetCellThreat(int x, int y, float threat)
    {
        mThreatArray[x, y].mPendingThreat = threat;
    }

    public void SetCellDampening(int x, int y, float dampening)
    {
        mThreatArray[x, y].mThreatDampening = dampening;
        mBufferedThreatArray[x, y].mThreatDampening = dampening;
    }
}
